package hu.mapro.gamelib;

import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;

public class SectionedLinearLevel extends AbstractLinearLevel {

	final Generator generator;
	final RearBlocker rearBlocker;
	
	final float frontVisibility;
	final float rearVisibility;
	final float minimumRetreatDistance;

	public SectionedLinearLevel(
			float minLateral, 
			float maxLateral,
			Generator generator, 
			RearBlocker rearBlocker, 
			float frontVisibility, 
			float rearVisibility,
			float minimumRetreatDistance,
			float startAxialPosition
	) {
		super(minLateral, maxLateral);
		this.generator = generator;
		this.rearBlocker = rearBlocker;
		this.frontVisibility = frontVisibility;
		this.rearVisibility = rearVisibility;
		this.minimumRetreatDistance = minimumRetreatDistance;
		
		init(startAxialPosition);
	}

	float rearVisibilityWithRetreat;
	float nextRearEndpoint;
	float rearSectionWidth;
	float frontEndpoint;
	float rearBlockerPosition;
	
	ShapeRenderer debugShapeRenderer = new ShapeRenderer();

	//final Queue<Section> sectionQueue = new ArrayDeque<SectionedLinearLevel.Section>();

	public interface Generator {
		
		float generateNextFrontSection();
		
		// returns width of new rear section
		float destroyRearSection();
		
		void flush();

		void render();
		
	}
	
//	public interface Section {
//		
//		float getWidth();
//		
//		void destroy();
//		
//	}

	@Override
	public void update(float axialPosition) {
		
		boolean changed = cleanRear(axialPosition);
		
		if (changed) {
			rearBlocker.moveRearBlock(rearBlockerPosition);
		}

		changed = changed || generateFront(axialPosition);
		
		if (changed) {
			generator.flush();
		}
	}

	public boolean generateFront(float axialPosition) {
		boolean changed = false;
		
		float frontEndOfSight = axialPosition + frontVisibility;
		while (frontEndOfSight > frontEndpoint) {
			changed = true;
			
			//Section frontSection = generator.next();
			frontEndpoint += generator.generateNextFrontSection();
			//sectionQueue.add(frontSection);
		}
		return changed;
	}

	public boolean cleanRear(float axialPosition) {
		boolean changed = false;
		
		float rearEndOfSight = axialPosition - rearVisibilityWithRetreat;
		while (rearEndOfSight > nextRearEndpoint) {
			changed = true;
			
			rearBlockerPosition += rearSectionWidth;
			rearSectionWidth = generator.destroyRearSection();
			nextRearEndpoint += rearSectionWidth;
		}
		return changed;
	}

	@Override
	public void render() {
		generator.render();
		
//		debugShapeRenderer.setProjectionMatrix(camera.combined);
//		debugShapeRenderer.begin(ShapeType.Line);
//		debugShapeRenderer.setColor(Color.RED);
//		debugShapeRenderer.line(nextRearEndpoint-rearSectionWidth, 0, frontEndpoint, 0);
//		for (float x = MathUtils.floor(nextRearEndpoint-rearSectionWidth) ; x <= frontEndpoint ; x++) {
//			debugShapeRenderer.line(x, Math.min(maxLateral,1000), x, minLateral);
//		}
//		
//		debugShapeRenderer.end();
	}

	public interface RearBlocker {
		
		void initRearBlock(float axialBlockPosition);
		
		void moveRearBlock(float axialBlockPosition);
		
	}
	
	public static final RearBlocker NULL_REAR_BLOCKER = new RearBlocker() {
		@Override
		public void moveRearBlock(float axialBlockPosition) {
		}
		@Override
		public void initRearBlock(float axialBlockPosition) {
		}
	};

	public void init(float startAxialPosition) {
		rearVisibilityWithRetreat = minimumRetreatDistance + rearVisibility;
		
		if (startAxialPosition < rearVisibilityWithRetreat) {
			throw new RuntimeException("not enough visibility to retreat");
		}

		rearBlockerPosition = rearVisibility;
		rearBlocker.initRearBlock(rearBlockerPosition);
		
		frontEndpoint = generator.generateNextFrontSection();
		rearSectionWidth = frontEndpoint;
		nextRearEndpoint = rearSectionWidth;
		
		generateFront(startAxialPosition);
		
		
		cleanRear(startAxialPosition);
		
		generator.flush();
	}
	
	public void dispose() {
		debugShapeRenderer.dispose();
	}
	
}
